package com.xiaoshuidi.cloud.module.finance.controller.admin.notify;

import com.alibaba.fastjson.JSONObject;
import com.xiaoshuidi.cloud.framework.common.enums.CommonStatusEnum;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClient;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClientFactory;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayCommonResult;
import com.xiaoshuidi.cloud.framework.pay.core.client.dto.PayNotifyDataDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.EnterPriseNotifyRespDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.WithdrawApplyNotifyRespDTO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.merchant.PayMerchantDO;
import com.xiaoshuidi.cloud.module.finance.service.merchant.PayMerchantService;
import com.xiaoshuidi.cloud.module.finance.service.order.PayOrderService;
import com.xiaoshuidi.cloud.module.finance.service.refund.PayRefundService;
import com.xiaoshuidi.cloud.module.finance.service.withdraw.FinanceThirdWithdrawService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND;

@Tag(name = "管理后台 - 支付通知")
@RestController
@RequestMapping("/pay/notify")
@Validated
@Slf4j
public class PayNotifyController {
    @Resource
    private PayOrderService orderService;
    @Resource
    private PayRefundService refundService;
    @Resource
    private PayClientFactory payClientFactory;

    @Resource
    private PayMerchantService payMerchantService;

    @Resource
    private FinanceThirdWithdrawService financeThirdWithdrawService;

    /**
     * 统一的跳转页面，支付宝跳转参数说明
     *
     * <a href="https://opendocs.alipay.com/open/203/105285#前台回跳参数说明">支付宝 - 前台回跳参数说明</a>
     *
     * @param channelId 渠道编号
     * @return 返回跳转页面
     */
    @GetMapping(value = "/return/{channelId}")
    @Operation(summary = "渠道统一的支付成功返回地址")
    @Deprecated // TODO yunai：如果是 way 的情况，应该是跳转回前端地址
    public String returnCallback(@PathVariable("channelId") Long channelId,
                                 @RequestParam Map<String, String> params) {
        log.info("[returnCallback][app_id({}) 跳转]", params.get("app_id"));
        return String.format("渠道[%s]支付成功", channelId);
    }

    /**
     * 统一的渠道支付回调，支付宝的退款回调
     *
     * @param channelId 渠道编号
     * @param params    form 参数
     * @param body      request body
     * @return 成功返回 "success"
     */
    @PostMapping(value = "/callback/{channelId}")
    @Operation(summary = "支付渠道的统一回调接口 - 包括支付回调，退款回调")
    @PermitAll
    public String notifyCallback(@PathVariable("channelId") Long channelId,
                                 @RequestParam(required = false) Map<String, String> params,
                                 @RequestBody(required = false) String body,
                                 HttpServletResponse response) throws Exception {
        log.info("[notifyCallback][channelId({}) 回调数据({}/{})]", channelId, params, body);
        // 1. 校验支付渠道是否存在
        PayClient payClient = payClientFactory.getPayClient(channelId);
        if (payClient == null) {
            log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
            throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
        }

        // 2. 解析通知数据
        PayNotifyDataDTO rawNotify = PayNotifyDataDTO.builder().params(params).body(body).build();
        PayOrderNotifyRespDTO notify;
        try {
            notify = payClient.parseOrderNotify(rawNotify);
//            notify = new PayOrderNotifyRespDTO().setMerchantOrderId("1").setNotifyStatus("SUCCESS");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 3.2：支付通知
        orderService.notifyPayOrder(channelId, notify, rawNotify);
        response.setCharacterEncoding("UTF-8");
        response.getOutputStream().write("success".getBytes());
        response.flushBuffer();
        return notify.getNotifyStatus();

        // 3. 处理通知
        // 3.1：退款通知
//        if (notify instanceof PayRefundNotifyDTO) {
//            refundService.notifyPayRefund(channelId, (PayRefundNotifyDTO) notify, rawNotify);
//            return "success";
//        }
    }

    @PostMapping(value = "/tongshangyun/callback/{channelId}", headers = "Content-Type=application/x-www-form-urlencoded")
    @Operation(summary = "通商云相关回调接口")
    @PermitAll
    public String notifyMerchant(
            @PathVariable("channelId") Long channelId,
            HttpServletRequest request,
            @RequestParam(required = false) Map<String, String> params,
            HttpServletResponse response) throws Exception {
        log.info("[notifyMerchant] 回调数据({}/{}/{})]", JSONObject.toJSONString(request.getParameterMap())
                , params);

        String bizContent = params.get("bizContent");
        String notifyType = params.get("notifyType");
        //验签
        //to do
        PayNotifyDataDTO rawNotify = PayNotifyDataDTO.builder().params(params).build();
        PayClient client = payClientFactory.getPayClient(channelId);
        PayOrderNotifyRespDTO payOrderNotifyRespDTO = client.parseOrderNotify(rawNotify);
        if (!payOrderNotifyRespDTO.isSuccess()) {
            log.error("验签失败=================");
            return "ERROR";
        }
        //"registerCompanyMemberResult"
        if ("allinpay.yunst.memberService.registerCompanyMemberResult".equals(notifyType)) {
            EnterPriseNotifyRespDTO result = JsonUtils.parseObject(bizContent, EnterPriseNotifyRespDTO.class);
            if (result.getStatus() == 2) {
                log.info("H5企业{}开户审核成功===============", result.getCompanyName());
            } else {
                log.error("H5企业{}开户审核失败===============", result.getCompanyName());
            }
            PayMerchantDO payMerchantDO = new PayMerchantDO();
            payMerchantDO.setReviewStatus(result.getStatus());
            BeanUtils.copyProperties(result, payMerchantDO);
            try {
                PayCommonResult<String> payCommonResult = client.queryBankCard(result.getBizUserId());
                if (payCommonResult.isSuccess()) {
                    String card = payCommonResult.getData();
                    log.info("{}设置银行卡卡号为:{}",payMerchantDO.getName(),card);
                    payMerchantDO.setBankCardNo(card);
                }
            } catch (Exception e) {
                log.error("查询银行卡卡号异常", e.getMessage());
            }
            payMerchantDO.setName(result.getCompanyName())
                    .setReviewStatus(result.getStatus())
                    .setAddress(result.getCompanyAddress())
                    //状态被覆盖
                    .setStatus(CommonStatusEnum.ENABLE.getStatus());
            payMerchantService.notifyMerchant(result.getUserId(),payMerchantDO);
        } else if ("allinpay.yunst.orderService.pay".equals(notifyType)) {
            WithdrawApplyNotifyRespDTO result = JsonUtils.parseObject(bizContent, WithdrawApplyNotifyRespDTO.class);
            if (result.getStatus().equals("OK")) {
                log.info("提现单号{}提现成功===============", result.getBizOrderNo());
            } else {
                log.error("提现单号{}提现失败===============", result.getBizOrderNo());
            }
            financeThirdWithdrawService.notifyWithDraw(result);
        } else {
            //todo
            PayOrderNotifyRespDTO notify = null;
            // 3.2：支付通知
            orderService.notifyTongshangyunPayOrder(channelId, notify, rawNotify);
            response.setCharacterEncoding("UTF-8");
            response.getOutputStream().write("success".getBytes());
            response.flushBuffer();
            return notify.getNotifyStatus();
        }
        return "SUCCESS";
    }
}
